﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using LockCrypt.Core;

namespace LockCrypt.DAL {
    public class AutomaticParser {
        /// <summary>
        /// Attempts to guess the encryption method and filetype of a file and instanciate a database provider.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <param name="logger">The logger.</param>
        /// <returns>A database provider which can open the file or <c>null</c>.</returns>
        public static IReadOnlyDatabase GetDatabaseProvider(string filename, ILogProvider logger) {
            if(!File.Exists(filename)) {
                throw new LockCryptException(I18nUtils.GetString("Errors", "FileDoesNotExist"), 232);
            }
            if(logger == null) {
                logger = new ConsoleLogger();
            }
            string password = string.Empty;
            try {
                logger.Log("Trying SQLite provider (No encryption)...", StatusMessage.StatusMessageType.Status);
                var sqliteProvider = new SQLiteProvider(filename, new NullEncryptionService());
                if(sqliteProvider.CheckPassword()) {
                    logger.Log("Detected SQLite database", StatusMessage.StatusMessageType.Success);
                    return sqliteProvider;
                } else {
                    logger.Log("Invalid password or not an SQLite database", StatusMessage.StatusMessageType.Error);
                }
            } catch(Exception ex) {
                Console.WriteLine(ex.Message);
                logger.Log("Invalid password or not an SQLite database", StatusMessage.StatusMessageType.Error);
            }
            try {
                logger.Log("Trying SQLite provider (AES 256 encryption)...", StatusMessage.StatusMessageType.Status);
                var sqliteProvider = new SQLiteProvider(filename, new Aes256EncryptionService(password));
                if(sqliteProvider.CheckPassword()) {
                    logger.Log("Detected SQLite database", StatusMessage.StatusMessageType.Success);
                    return sqliteProvider;
                } else {
                    logger.Log("Invalid password or not an SQLite database", StatusMessage.StatusMessageType.Error);
                }
            } catch(Exception ex) {
                Console.WriteLine(ex.Message);
                logger.Log("Invalid password or not an SQLite database", StatusMessage.StatusMessageType.Error);
            }

            try {
                logger.Log("Trying XML provider (No encryption)...", StatusMessage.StatusMessageType.Status);
                var xmlProvider = new XmlProvider(filename, new NullEncryptionService());
                if(xmlProvider.CheckPassword()) {
                    logger.Log("Detected XML file", StatusMessage.StatusMessageType.Success);
                    return xmlProvider;
                } else {
                    logger.Log("Invalid password or not an XML file", StatusMessage.StatusMessageType.Error);
                }
            } catch(Exception ex) {
                Console.WriteLine(ex.Message);
                logger.Log("Invalid password or not an XML file", StatusMessage.StatusMessageType.Error);
            }
            try {
                logger.Log("Trying XML provider (AES 256 encryption)...", StatusMessage.StatusMessageType.Status);
                var xmlProvider = new XmlProvider(filename, new Aes256EncryptionService(password));
                if(xmlProvider.CheckPassword()) {
                    logger.Log("Detected XML file", StatusMessage.StatusMessageType.Success);
                    return xmlProvider;
                } else {
                    logger.Log("Invalid password or not an XML file", StatusMessage.StatusMessageType.Error);
                }
            } catch(Exception ex) {
                Console.WriteLine(ex.Message);
                logger.Log("Invalid password or not an XML file", StatusMessage.StatusMessageType.Error);
            }

            try {
                logger.Log("Trying LockCrypt legacy provider...", StatusMessage.StatusMessageType.Status);
                var legacyProvider = new LegacyProvider(filename);
                if(legacyProvider.CheckPassword()) {
                    logger.Log("Detected LockCrypt legacy database", StatusMessage.StatusMessageType.Success);
                    return legacyProvider;
                } else {
                    logger.Log("Invalid password or not a legacy database", StatusMessage.StatusMessageType.Error);
                }
            } catch(Exception ex) {
                Console.WriteLine(ex.Message);
                logger.Log("Invalid password or not a legacy database", StatusMessage.StatusMessageType.Error);
            }

            try {
                logger.Log("Trying LockCrypt legacy XML provider...", StatusMessage.StatusMessageType.Status);
                var legacyXmlProvider = new LegacyXMLParser(filename, logger);
                if(legacyXmlProvider.CanLoadContents()) {
                    logger.Log("Detected LockCrypt legacy XML database", StatusMessage.StatusMessageType.Success);
                    return legacyXmlProvider;
                } else {
                    logger.Log("Invalid password or not a legacy XML database", StatusMessage.StatusMessageType.Error);
                }
            } catch(Exception ex) {
                Console.WriteLine(ex.Message);
                logger.Log("Not a legacy XML database", StatusMessage.StatusMessageType.Error);
            }

            try {
                logger.Log("Trying Keepass 2.x XML File provider...", StatusMessage.StatusMessageType.Status);
                var legacyXmlProvider = new Keepass2xParser(filename, logger);
                if(legacyXmlProvider.CanLoadContents()) {
                    logger.Log("Detected Keepass 2.x XML File", StatusMessage.StatusMessageType.Success);
                    return legacyXmlProvider;
                } else {
                    logger.Log("Invalid password or not a Keepass 2.x XML File", StatusMessage.StatusMessageType.Error);
                }
            } catch(Exception ex) {
                Console.WriteLine(ex.Message);
                logger.Log("Not a Keepass 2.x XML File", StatusMessage.StatusMessageType.Error);
            }

            logger.Log("File type not recognized", StatusMessage.StatusMessageType.Error);
            return null;
        }

    }
}
